/** @jsxImportSource @emotion/react */
import {jsx,css} from "@emotion/react";
import * as React from "react";
import { Text } from "./Text";
import { CloseButton } from "./IconButton";
import { useTheme } from "./Theme/Providers";
import {
    useFloating,
    useClick,
    useDismiss,
    useRole,
    useInteractions,
    FloatingPortal,
    FloatingFocusManager,
    FloatingOverlay,
    useId, useMergeRefs
} from "@floating-ui/react";
import {CSSProperties} from "react";



interface DialogOptions {
    //非受控方式的初始值
    initialOpen?: boolean;
    //注入控制器的方式：实时取值
    open?: boolean;
    onOpenChange?: (open: boolean) => void;
    //是不是必须手动关闭，点击外部区域的不会自动关闭;  true=不会自动关闭！；
    modal?: boolean;
}

export function useDialog({
                              initialOpen = false,modal=true,
                              open: controlledOpen,
                              onOpenChange: setControlledOpen
                          }: DialogOptions = {}) {
    const [uncontrolledOpen, setUncontrolledOpen] = React.useState(initialOpen);
    const [labelId, setLabelId] = React.useState<string | undefined>();
    const [descriptionId, setDescriptionId] = React.useState<
        string | undefined
    >();

    const open = controlledOpen ?? uncontrolledOpen;
    const setOpen = setControlledOpen ?? setUncontrolledOpen;

    const data = useFloating({
        open,
        onOpenChange: setOpen
    });

    const context = data.context;

    const click = useClick(context, {
        enabled: controlledOpen == null
    });
    //确保不会因为点击对话框区域之外立刻关闭地：//modal={state.modal}
    const dismiss = useDismiss(context, {
        outsidePressEvent: "mousedown",
        outsidePress:  !modal,
    });
    // const dismiss = useDismiss(context, { outsidePressEvent: "mousedown" });
    const role = useRole(context);

    const interactions = useInteractions([click, dismiss, role]);

    return React.useMemo(
        () => ({
            open,
            setOpen,
            ...interactions,
            ...data,
            labelId,
            descriptionId,
            setLabelId,
            setDescriptionId,
            modal,
        }),
        [open, setOpen, modal, interactions, data, labelId, descriptionId]
    );
}

type ContextType =
    | (ReturnType<typeof useDialog> & {
    setLabelId: React.Dispatch<React.SetStateAction<string | undefined>>;
    setDescriptionId: React.Dispatch<
        React.SetStateAction<string | undefined>
    >;
})
    | null;

const DialogContext = React.createContext<ContextType>(null);
//旧版本的 useDialogState
export const useDialogContext = () => {
    const context = React.useContext(DialogContext);

    if (context == null) {
        throw new Error("Dialog components must be wrapped in <Dialog />");
    }

    return context;
};

export function Dialog({
                           children,
                           ...options
                       }: {
    children: React.ReactNode;
} & DialogOptions) {
    const dialog = useDialog(options);
    return (
        <DialogContext.Provider value={dialog}>{children}</DialogContext.Provider>
    );
}

interface DialogTriggerProps {
    children: React.ReactElement<any>;       //不能再用React.ReactNode ；后面.props.ref无法用的;
    asChild?: boolean;
}

export const DialogTrigger =
    ({ children, asChild = false, ref, ...props }:React.HTMLProps<HTMLElement> & DialogTriggerProps)=> {
    const context = useDialogContext();
    const childrenRef = children.props.ref;             //const childrenRef = (children as any).ref;
    const refnew = useMergeRefs([context.refs.setReference, ref, childrenRef]);

    // `asChild` allows the user to pass any element as the anchor
    if (asChild && React.isValidElement(children)) {
        return React.cloneElement(
            children,
            context.getReferenceProps({
                ref: refnew,
                ...props,
                ...(children.props as any),
                "data-state": context.open ? "open" : "closed"
            })
        );
    }

    return (
        <button
            ref={refnew}
            // The user can style the trigger based on the state
            data-state={context.open ? "open" : "closed"}
            {...context.getReferenceProps(props)}
        >
            {children}
        </button>
    );
};

interface OverlayOptions {
    //OverLay最上一级的样式; 【极特殊情形】地图不要Y轴线的滚动了。地图全屏也不要任何滚动条。
    layStyle?: CSSProperties;
}

export const DialogContent =
    ({layStyle, ref, ...props}:React.HTMLProps<HTMLDivElement> & OverlayOptions)=> {
    const { context: floatingContext, ...context } = useDialogContext();
    const refnew = useMergeRefs([context.refs.setFloating, ref]);
    const theme = useTheme();
    if (!floatingContext.open) return null;

    return (
        <FloatingPortal>
            <FloatingOverlay className="Dialog-overlay" lockScroll
                             style={{
                                 zIndex: theme.zIndices.modal,
                                 display: 'flex',
                                 overflow: 'unset',     //在FloatingOverlay内部默认有了
                                 overflowX: 'hidden',
                                 ...layStyle,
                             }}
            >
                <FloatingFocusManager context={floatingContext}
                    // modal={floatingContext.modal}
                                      closeOnFocusOut={false}  guards={true}  returnFocus={true}>
                    <div className="DialogContent"
                         ref={refnew}
                         aria-labelledby={context.labelId}
                         aria-describedby={context.descriptionId}
                         {...context.getFloatingProps(props)}
                         style={{
                             display: 'flex',
                             margin: 'auto',
                             overflowY: 'auto',
                             //这上[theme.mediaQueries.md]: {}的会报错! width放在style={{里导致无法从外面注入到这底下的css={{来覆盖！
                         }}
                    >
                        <div className="DialogIn"
                             style={{
                                 backgroundColor: 'white',
                                 minHeight: "30vh",
                             }}
                             css={
                                 {
                                     width: "100%",
                                     border: '2px solid #ddd',
                                     borderRadius: theme.radii.sm,
                                     boxShadow: theme.shadows.sm,
                                     padding: '4px',
                                     [theme.mediaQueries.md]: {
                                         margin: '1rem',
                                         border: '3px solid #ddd',
                                         padding: '8px',
                                         maxWidth: "760px",
                                         boxShadow: theme.shadows.md,
                                         borderRadius: theme.radii.md,
                                     },
                                     [theme.mediaQueries.lg]: {
                                         maxWidth: "990px",
                                         boxShadow: theme.shadows["lg"],
                                         borderRadius: theme.radii.lg,
                                     },
                                     "@media print": {
                                         boxShadow: 'unset',
                                     },
                                     ...props.style,
                                 }
                             }
                        >
                            {props.children}
                        </div>
                    </div>
                </FloatingFocusManager>
            </FloatingOverlay>
        </FloatingPortal>
    );
};

interface DialogHeadingOptions {
    //标题文本替换成组件
    asChild?: boolean;
    //右顶头"X"关闭按钮样式
    closeStyle?: CSSProperties;
}
/*
这底下添加tabIndex={0}才能消除了此毛病：内容超过一个屏幕高度的话，就感觉古怪，直接跳到最后，无法正面显示头部，还要滚动。
* */
export const DialogHeading =
    ({children,asChild = false,closeStyle, ref, ...props}:React.HTMLProps<HTMLHeadingElement> & DialogHeadingOptions)=> {
    const {setLabelId, setOpen} = useDialogContext();
    const id = useId();

    // Only sets `aria-labelledby` on the Dialog root element
    // if this component is mounted inside it.
    React.useLayoutEffect(() => {
        setLabelId(id);
        return () => setLabelId(undefined);
    }, [id, setLabelId]);

    //支持更自由的自定义头部：
    const textComp= (asChild && React.isValidElement(children) ) ?
        React.cloneElement(
            children,
            { ...(children.props as any), }
        )
        :
        <Text wrap={true} variant="h5">
            {children}
        </Text>;
    return (
        <div  ref={ref} id={id} tabIndex={-1}  autoFocus={false}
              style={{
                  justifyContent: 'space-between',
                  display: 'flex',
              }}
              {...props}
        >
            {textComp}
            <CloseButton
                style={{
                    ...closeStyle,
                }}
                onClick={() => setOpen(false)}
            />
        </div>
    );
};

export const DialogDescription =
({children, ref, ...props}:React.HTMLProps<HTMLParagraphElement>)=> {
    const {setDescriptionId} = useDialogContext();
    const id = useId();

    // Only sets `aria-describedby` on the Dialog root element
    // if this component is mounted inside it.
    React.useLayoutEffect(() => {
        setDescriptionId(id);
        return () => setDescriptionId(undefined);
    }, [id, setDescriptionId]);

    return (
        <div {...props} ref={ref} id={id}>
            {children}
        </div>
    );
};

//原本类型:React.ButtonHTMLAttributes<HTMLButtonElement>
export const DialogClose =
    ({ children, ref, ...props }: (React.HTMLProps<HTMLButtonElement> & React.ButtonHTMLAttributes<HTMLButtonElement>))=> {
    const {setOpen} = useDialogContext();
    return (
        <button {...props} ref={ref}
                onClick={() => setOpen(false)}
                css={{
                    marginTop: '0.5rem',
                    marginLeft: '1rem',
                }}
        >
            {children}
        </button>
    );
    // return (<button type="button" {...props} ref={ref} onClick={() => setOpen(false)}/>);
};


/*根据例子修改的 https://codesandbox.io/s/charming-bush-47epk2?file=/src/App.tsx
注入控制模式
  const [open, setOpen] = useState(false);
  useEffect(() => {
    const timeout = setTimeout(() => {
      setOpen(true);
    }, 2000);
    return () => clearTimeout(timeout);
  }, []);
  return (
    <div className="App">
      <h1>Floating UI — Dialog</h1>
      <p>The dialog will open in 2 seconds...</p>
      <Dialog open={open} onOpenChange={setOpen}>
        <DialogContent className="Dialog">
          <DialogHeading>I opened automatically</DialogHeading>
          <DialogDescription>After 2 seconds</DialogDescription>
          <DialogClose>Close</DialogClose>
        </DialogContent>
      </Dialog>
    </div>
  );
* */
